<template>
  <div :style="{ width: width }">
    <el-popover
      popper-class="select-load-popover"
      :placement="placement"
      trigger="click"
      :disabled="disabled"
      @after-leave="onPopoverAfterLeave"
      v-model="popoverShow"
    >
      <template slot="reference">
        <el-input
          class="select-load-input"
          :size="size"
          debounce="500"
          v-model="inputValue"
          :disabled="disabled"
          :readonly="readonly"
          :placeholder="placeholder"
          @focus="onFocus"
          @blur="onBlur"
          @input.native="onInput"
          @keyup.native.38="customizeItem ? null : inputOnUp()"
          @keyup.native.40="customizeItem ? null : inputOnDown()"
          @keyup.native.enter="inputOnEnter"
        >
          <template slot="suffix">
            <i
              class="input-icon input-icon-del el-icon-delete"
              key="1"
              v-if="inputValue && clearable"
              @click.stop="onClear"
            ></i>
            <i
              class="input-icon el-icon-arrow-down"
              key="2"
              :class="{ 'input-icon-down': popoverShow }"
              @click.stop="onClickDown"
            ></i>
          </template>
        </el-input>
      </template>
      <div class="header-box" v-if="$slots['header']">
        <slot name="header"></slot>
      </div>
      <!-- 表格结构 -->
      <el-table
        :data="selectList"
        :size="size"
        ref="table"
        max-height="250"
        style="max-width:600px"
        v-if="!customizeItem"
        highlight-current-row
        :current-row-key="code"
        v-loading="loading || selectLoading"
        :row-class-name="rowClassName"
        @row-click="rowClick"
        @row-dblclick="onItemDblclick"
      >
        <el-table-column
          type="index"
          label="#"
          width="50"
          v-if="$slots.default()['table-column']&&!hiddenIndex"
        ></el-table-column>
        <slot name="table-column">
          <el-table-column :prop="code" label="编号" width="100"></el-table-column>
          <el-table-column :prop="name" label="名称" min-width="120"></el-table-column>
        </slot>
      </el-table>
      <!--自定义结构-->
      <div class="select-list" v-else v-loading="loading || selectLoading">
        <div
          v-for="(item, index) in selectList"
          :key="item[code]"
          @click="clickItem(item, index)"
          @dblclick="dblclickItem(item, index)"
          class="item"
        >
          <slot name="item" :item="item"></slot>
        </div>
        <!-- <el-tag
          v-for="(item, index) in filterable(inputValue)"
          :key="index"
          @click="clickLabelTag(item,index)"
          @dblclick="rowDblclick(item,index)"
          :effect="item.select === true ? 'dark' : 'plain'"
        >{{ item.Name }}</el-tag>-->
        <!-- <span v-if="selectList.length === 0" class="none">暂无数据</span> -->
      </div>
      <!--是否展示分页-->
      <div class="footer-page" v-if="thisIsPaging">
        <el-pagination
          @current-change="handleCurrentChange"
          :current-page="page"
          :page-size="limit"
          layout="total, prev, pager, next, jumper"
          :total="total"
        ></el-pagination>
      </div>
    </el-popover>
  </div>
</template>

<script lang="ts">
import { Component, Vue, Watch, Prop } from "vue-property-decorator";
import { getSingleList, getListAndTotal } from "@/utils/api";
import { find } from "@/library/util";

import { Popover, Pagination, Table, TableColumn, Input } from "element-ui";

@Component({
  components: {
    "el-popover": Popover,
    "el-pagination": Pagination,
    "el-table": Table,
    "el-table-column": TableColumn,
    "el-input": Input
  }
})
export default class SelectLoad extends Vue {
  /**
   * ===============================
   *  全局设置
   * ===============================
   */
  /**
   * ==============
   *    输入参数
   * ==============
   */
  /**
   * 输入值
   */
  @Prop({
    default: ""
  })
  readonly value!: any;

  /**
   * 是否被禁用
   */
  @Prop({
    default: false
  })
  //   readonly disabled?: boolean;
  readonly disabled!: boolean;

  /**
  弹框的显示位置
   */
  @Prop({
    default: "bottom-start",
    validator(val: string) {
      return (
        [
         
        ].indexOf(val) >= 0
      );
    }
  })
  //   readonly disabled?: boolean;
  readonly placement!: string;

  /**
   * 提示话术
   */
  @Prop({
    type: String,
    default: "请选择"
  })
  readonly placeholder!: string;

  /**
   * 是否可以自定义输入内容
   */
  @Prop({
    type: Boolean,
    default: false
  })
  readonly customInput!: boolean;

  /**
   * 是否可以清空选项
   */
  @Prop({
    type: Boolean,
    default: true
  })
  readonly clearable!: boolean;

  /**
   * 是否只读
   */
  @Prop({
    default: false
  })
  readonly readonly?: boolean;

  /**
   * 默认是Name和Code下方传递的code和name可用来指定哪个字段为code或者name
   */
  @Prop({
    type: String,
    default: "Code"
  })
  readonly code!: string;

  /**
   *  自定义业务Name
   */
  @Prop({
    type: String,
    default: "Name"
  })
  readonly name!: string;

  /**
 自定义展示格式
    这个需要传入的方法，参数是row
    这个方法必须有有一个string类型返回值，用作展示。
    注意：当同时使用nameValue的时候，需要对nameValue传入的进行相同的处理
    不然nameValue会覆盖输入框的
    */
  @Prop({
    default: undefined
  })
  readonly displayFormat!: any;

  /**
   * 是否使用静态数据 ， 及使用本地值
   */
  @Prop({
    default: false
  })
  readonly isStatic!: boolean;

  /**
   * 静态传入的列表数据
   * 当需要开启isStatic为true
   */
  @Prop({
    type: Array,
    default() {
      return [];
    }
  })
  readonly staticList!: any[];

  /**
   * 是否开启搜索
   * 默认为开启
   */
  @Prop({
    default: true
  })
  readonly isSearch!: boolean;

  /**
   * 开启强制更新
   * 是每次获取焦点都加载（默认不是每次都加载）
   * 默认为false
   */
  @Prop({
    default: false
  })
  //   readonly isLoading?: boolean;
  readonly forceUpdate?: boolean;

  /**
   * 业务类型
   * 当使用isStatic为true的时候，不调用
   */
  @Prop({
    type: String
  })
  readonly docType!: string;

  /**
   * 业务次级类型
   */
  @Prop({
    type: String
  })
  readonly actionType!: string;

  /**
   * 新版接口index
   */
  @Prop({
    type: Number,
    default: 1
  })
  readonly hostIndex!: number;

  /**
   * 加载中
   */
  @Prop({
    type: Boolean,
    default: false
  })
  readonly loading!: boolean;
  
  /**
   * 自定义远程获取事件
   * 传入参数
   * {
   *    search: 当前搜索框数据
   *    page:当前页数
   *    limit:每页的数量
   * }
   * 这个事件必须返回一个Promise
   * 返回的数据中需要提供
   * {
   *    dataList:数据列表
   *    total:数据个数
   *    isPaging:是否分页   当不反回这个值的时候，将使用Prop中的isPaging
   * }
   */
  @Prop({
    type: Function,
    default: undefined
  })
  readonly remoteMethod!: any;

  /**
   * 模糊搜索字数限制,达到这个字数才会触发搜索
   */
  @Prop({
    type: Number,
    default: 0
  })
  readonly wordLimit!: number;

  /**
   * 请求列表需要单独传递id时传递的参数
   */
  @Prop({
    type: [String, Number],
    default: ""
  })
  readonly Id?: string | number;

  /**
   *请求列表需要传多个参数时可以在此对象中传递{'p1':p1}
   */
  @Prop({
    type: Object,
    default() {
      return {};
    }
  })
  readonly dataObj!: any;

  /**
   *是否加载分页数据
   */
  @Prop({
    default: false
  })
  readonly isPaging!: boolean;

  /**
   * 自定义条数
   */
  @Prop({
    type: Number,
    default: 25
  })
  readonly limit!: number;

  /**
   * 传入的name值(输入框展示值)
   * 这个通常用在分页列表中
   */
  @Prop({
    default: ""
  })
  readonly nameValue!: string;

  /**
   * 自定义个体item
   */
  @Prop({
    type: Boolean,
    default: false
  })
  readonly customizeItem!: boolean;

  /**
   * 选择下拉框宽度设置
   */
  @Prop({
    default: "100%"
  })
  readonly width?: string;

  /**
   * 大小
   */
  @Prop({
    default: "mini"
  })
  readonly size?: string;

  /**
   * 是否隐藏index 列
   */
  @Prop({
    default: false
  })
  readonly hiddenIndex?: boolean;

  /**
   * ==============
   *    自定义参数
   * ==============
   */
  /**
   * 这个是自定义的是否使用分页
   * 当使用自定义远程数据的时候，我们有可能是通过
   * 自定义远程数据返回的数据才能决定是否分页
   */
  thisIsPaging: boolean = this.isPaging;

  /**
   * 当前组件使用的value
   */
  thisValue: any = this.value || "";

  /**
   * -----当前组件使用的nameValue
   */
  thisNameValue: string = this.nameValue;

  /**
   * input的输入值
   */
  inputValue: string = this.nameValue;

  /**
   * 单列表数据备份
   * 用于本地数据和获取的单列表数据
   */
  thisStaticList: any[] = [];

  /**
   *下拉框请求的列表内容
   */
  selectList: any = [];

  /**
   *加载下拉框列表的loading
   */
  selectLoading: boolean = false;

  /**
   * 当前下拉弹框是否展示
   */
  popoverShow: boolean = false;

  /**
   * ==============
   *    侦听器
   * ==============
   */
  /**
   * 输入值
   */
  @Watch("value", {
    immediate: true
  })
  valueWatch(val: any) {
    this.thisValue = val;
    // if (val || val === 0) {
    //2020年1月10日
    //id为0的时候就是空，因为id都是number类型的
    if (val) {
      this.binding();
      this.getData();
    } else {
      //置空
      this.onChange({});
    }
  }
  /**
   * 当前组件使用的value
   */
  @Watch("thisValue", {
    immediate: true
  })
  thisValueWatch(val: any) {
    // console.log(val, "thisValue");
    this.$emit("input", val);
  }

  /**
   * 监听  输入值
   */
  @Watch("inputValue", {
    immediate: true
  })
  thisInputValue(val: any) {
    // console.log(val, "inputValue");
    if (this.customInput) {
      this.thisValue = val;
    }
  }
  /**
   * 参数id
   */
  @Watch("Id")
  IdWatch(val: any) {
    //重新加已经加载  更改成未加载
    this.isLoad = false;
  }

  /**
   * 其他参数
   */
  @Watch("dataObj", {
    deep: true
  })
  dataObjWatch(val: any, olValue: any) {
    for (let x in val) {
      if (val[x] !== olValue[x]) {
        //重新加已经加载  更改成未加载
        this.isLoad = false;
        return;
      }
    }
  }

  @Watch("nameValue", {
    immediate: true
  })
  nameValueWatch(val: string) {
    // console.log(val, "nameValue");
    this.thisNameValue = val;
    this.inputValue = val;
  }

  /**
   * 静态数据
   */
  @Watch("staticList", {
    immediate: true
  })
  staticListWatch(val: any[]) {
    if (this.isStatic) {
      this.thisStaticList = val;
    }
  }

  /**
   * 单列表数据备份
   * 用于静态数据  和 单列表数据
   */
  @Watch("thisStaticList", {
    immediate: true
  })
  thisStaticListWatch(val: any[]) {
    // console.log(val, "thisStaticList");
    this.selectList = val;
    this.$emit("on-select-list", val);
    //2020年1月15日 添加了判断length大于0
    //这个是由于当custom-input 与 nameValue同时使用的时候
    //这里的binding就会 覆盖掉 name的赋值
    if (val.length > 0) {
      this.binding();
    }
  }
  /**
   * ==============
   *    方法事件
   * ==============
   */
  /**
   * 当input框获得焦点的时候
   */
  onFocus() {
    if (this.forceUpdate) {
      //开启强制更新的时候，每次获取焦点都将更新数据
      this.resetGetData();
    } else {
      this.getData();
    }
  }
  
  close() {
    this.popoverShow = false;
  }
  /**
   * 当input框失去焦点的时候
   */
  onBlur() {}

  /**
   * 当用户输入值的时候
   */
  onInput(e?: any) {
    if (!this.popoverShow && this.disabled !== true) {
      //如果下拉框没有展示的时候
      //且当前下拉框不为禁止状态的时候
      //开启下拉框
      this.popoverShow = true;
    }
    let val = e.target.value;
    if (this.customInput) {
      //当为可以自定义输入值的时候
      //用户输入的值也属于最终结果
      this.onSelectItem(
        {
          [this.code]: val + "",
          [this.name]: val + ""
        },
        {
          customInput: true
        }
      );
    }
    //启动搜索
    this.onSearch(val + "");
  }

  /**
   * 当table显示框关闭的时候
   */
  onPopoverAfterLeave() {
    this.hoverIndex = -1;
    if (this.$refs.table) {
      (this.$refs.table as any).setCurrentRow();
    }
    if (this.thisNameValue !== this.inputValue) {
      if (!this.customInput) {
        this.inputValue = this.thisNameValue;
        // console.log(this.inputValue + "");
        //这个时候强制搜索
        this.onSearch("");
      }
    }
  }

  /**
   * 当在input上的时候，按下回车的时候
   */
  inputOnEnter() {
    if (this.popoverShow) {
      if (!this.selectList[this.hoverIndex]) {
        this.hoverIndex = -1;
      } else {
        this.onSelectItem(this.selectList[this.hoverIndex]);
      }
      //弹框隐藏
      // this.popoverShow = false;
    } else {
      if (this.disabled !== true) {
        //当弹框没有出现的时候，就打开弹框
        this.popoverShow = true;
      }
    }
  }
  /**
   * 根据value进行对inputValue进行反选
   * 当value不到对应的selectList的时候，inputValue将显示value值
   */
  binding() {
    //当这个组件为自定义输入内容的时候，我们将不进行任何反选
    if (this.customInput) {
      this.inputValue = this.value;
    } else if (this.isPaging) {
      //当为分页加载数据的时候
      if (!this.inputValue) {
        this.inputValue = this.value;
      }
    } else {
      let list = this.selectList;
      let index = find(list, this.code, this.value);
      // console.log(index, this.value);
      this.$nextTick(() => {
        if (index >= 0) {
          this.onChange(list[index]);
        } else {
          this.inputValue = this.value;
        }
      });
    }
  }
  /**
   * 这个表示只要值发生更改就会调用
   */
  onChange(row: any) {
    //赋值到input框上
    this.inputValue = this.displayFormat
      ? this.displayFormat(row)
      : row[this.name] || "";
    //赋值到thisNameValue上
    this.thisNameValue = this.inputValue;
    let defaultValue: string | number = "";
    if (typeof this.thisValue == "number") {
      defaultValue = 0;
    }

    //赋值到外部
    this.thisValue = row[this.code] || defaultValue;
    this.$emit("change", row);
  }
  /**
   * 这个表示是由用户触发行为
   * 选择某个项目的时候
   */
  onSelectItem(row: any, { customInput = false } = {}) {
    this.onChange(row);
    // console.log(row,"select")
    this.$emit("select", row, { customInput });
    //弹框隐藏
    this.popoverShow = false;
  }
  /**
   * 点击输入框小箭头的时候
   */
  onClickDown() {
    if (this.disabled) return;
    if (!this.popoverShow) {
      this.popoverShow = true;
      this.onFocus();
    } else {
      this.popoverShow = false;
    }
  }
  /**
   * 输入框点击清空时触发的方法
   */
  onClear() {
    this.onSelectItem({});
    //清空搜索值
    this.onSearch("");
  }
  /**
   * ===============================
   *  搜索功能
   * ===============================
   */

  /**
   * 模糊搜索值
   */
  searchv: string = "";

  /**
   *当前已经缓存搜索的定时器
   */
  remoteTimeList: any[] = [];

  /**
   * 搜索入口
   */
  onSearch(value: string) {
    if (this.isSearch && value.length >= this.wordLimit) {
      //我们会在用户输入停止超过300毫秒的时候，再启动搜索
      //清空定时器
      for (let item of this.remoteTimeList) {
        clearTimeout(item);
      }
      this.remoteTimeList = [];
      let timeNum = setTimeout(() => {
        if (this.thisIsPaging) {
          //当为分页的时候，需要启动远程搜索
          this.longRangeSearch(value);
        } else {
          //本地搜索
          this.localRangeSearch(value);
        }
      }, 450);
      this.remoteTimeList.push(timeNum);
    }
  }
  /**
   * 远程搜索
   */
  longRangeSearch(value: string) {
    this.searchv = value;
    this.page = 1;
    this.resetGetData();
  }
  /**
   * 本地检索
   */
  localRangeSearch(value: string) {
    let list = this.thisStaticList.filter((item: any) => {
      //过滤数组元素
      return item[this.name] && item[this.name].includes(value);
    });
    // console.log(list);
    this.selectList = list;
  }
  /**
   * ===============================
   *  分页功能
   * ===============================
   */
  /**
   * 当前页数
   */
  page: number = 1;
  /**
   * 全部的商品数量
   */
  total: number = 0;

  /**
   * 页数改变的方法
   */
  handleCurrentChange(data: any) {
    this.page = data;
    this.resetGetData();
  }
  /**
   * ===============================
   *  数据获取
   * ===============================
   */
  /**
   * 表示服务器数据是否已经加载
   */
  isLoad: boolean = false;
  /**
   * 重新获取数据
   */
  public resetGetData() {
    this.isLoad = false;
    this.getData();
  }
  /**
   * 根据
   */
  /**
   * 获取列表
   */
  getData() {
    //当前只能获取一次，除非调用重新获取数据
    if (this.isLoad) {
      return;
    }
    this.isLoad = true;
    // if (this.isStatic) {
    //   //如果当前为静态数据，则直接调用静态数据
    //   this.selectList = this.staticList;
    //   this.binding();
    if (!this.isStatic) {
      //当前为通过服务器获取数据

      this.selectLoading = true;
      let data = {
        docType: this.docType,
        actionType: this.actionType,
        search: this.searchv,
        data: this.dataObj
      };
      /**
       * 当前存在自定义的远程过去数据方法的时候
       */
      if (this.remoteMethod) {
        this.remoteMethod(
          Object.assign(data, {
            page: this.page,
            limit: this.limit
          })
        )
          .then(
            (res: { dataList: any[]; total: number; isPaging?: boolean }) => {
              if (typeof res.isPaging == "boolean") {
                this.thisIsPaging = res.isPaging;
              }
              if (this.thisIsPaging) {
                //分页
                this.selectList = res.dataList;
                this.total = res.total;
              } else {
                //不分页
                this.thisStaticList = res.dataList;
              }
            }
          )
          .finally(() => {
            this.selectLoading = false;
          });
      } else {
        if (this.thisIsPaging === true) {
          // console.log(data);
          /**
           * 分页时的接口请求
           */
          getListAndTotal(
            Object.assign(data, {
              page: this.page,
              limit: this.limit,
              hostIndex: this.hostIndex
            })
          )
            .then((res: any) => {
              this.selectList = res.List;
              this.total = res.Total;
            })
            .finally(() => {
              this.selectLoading = false;
            });
        } else {
          /**
           * 单页时的接口请求
           */
          getSingleList(data)
            .then((res: any) => {
              this.thisStaticList = res.List;
              // this.selectFn();
            })
            .finally(() => {
              this.selectLoading = false;
            });
        }
      }
    }
  }
  /**
   * ===============================
   *  默认下拉框状态下 及 table布局
   * ===============================
   */

  /**
   * 当前常量的index值
   */
  hoverIndex: number = -1;
  /**
   * 当在input上的时候，按下上的时候
   */
  inputOnUp() {
    if (this.hoverIndex == -1) {
      this.hoverIndex = this.selectList.length - 1;
    } else if (this.hoverIndex == 0) {
      this.hoverIndex = this.selectList.length - 1;
    } else {
      this.hoverIndex--;
    }
    (this.$refs.table as any).setCurrentRow(this.selectList[this.hoverIndex]);
  }
  /**
   * 当某一行被单击的时候
   */
  rowClick(row: any) {
    if (!this.thisIsPaging) {
      //当为不分页应用的时候，单击即可选择
      if (this.popoverShow) {
        //保证 即便用户使用双击的时候  , 也只触发一次这个方法
        this.onSelectItem(row);
      }
      //弹框隐藏
      // this.popoverShow = false;
    }
    this.$emit("on-click-item", row);
  }
  /**
   * 当在input上的时候，按下下键的时候
   */
  inputOnDown() {
    if (this.hoverIndex == -1) {
      this.hoverIndex = 0;
    } else if (this.hoverIndex == this.selectList.length - 1) {
      this.hoverIndex = 0;
    } else {
      this.hoverIndex++;
    }
    (this.$refs.table as any).setCurrentRow(this.selectList[this.hoverIndex]);
  }
  /**
   * 当某一行被双击了的时候
   */
  onItemDblclick(row: any) {
    //当为分页应用的时候，需要双击才能够选择当前行
    if (this.thisIsPaging) {
      this.onSelectItem(row);
      //弹框隐藏
      // this.popoverShow = false;
    }
    this.$emit("on-dblclick-item", row);
  }
  /**
   * 行回调
   */
  rowClassName(prop: { row: any; rowIndex: number }) {
    prop.row._index = prop.rowIndex;
  }
  /**
   * ===============================
   *  默认下拉列表为自定义
   * ===============================
   */
  /**
   * 列表item单击事件
   */
  clickItem(row: any, index: number) {
    this.$emit("on-click-item", row, index);
    //console.log(data)
  }

  //双击事件暂时没有用到
  dblclickItem(row: any, index: number) {
    this.$emit("on-dblclick-item", row, index);
  }

  //==============================================

  /**
   * ===============================
   *  采用标签列表渲染时会用到的属性
   * ===============================
   */
  //是否采用标签式渲染
  @Prop({
    default: false
  })
  readonly isTag?: boolean;

  /**
   * ===============================
   *  分页功能
   * ===============================
   */
}
</script>

<style lang="less">
.select-load-dropdown {
  .el-select-dropdown__list {
    padding-top: 0 !important;
    padding-bottom: 0 !important;
    //选择栏
    .hover {
      background-color: var(--mainColorN) !important;
    }
  }
}
.select-load-input {
  // .input选择框
  .el-input__inner {
    padding-right: 50px !important;
  }
  // .el-input__suffix-inner {
  //   background-color: white;
  // }
}
// 表结构
.select-load-popover {
  padding: 0px;
  border: 1px solid rgba(0, 0, 0, 0.1);

  // 选中行
  .select-row {
    color: var(--mainColor);
    font-weight: bold;
  }
}

.select-list {
  .el-tag {
    margin: 0 10px 10px 0;
    cursor: pointer;
  }
}
</style>

<style scoped lang="less">
// 表头
.select-head {
  padding: 5px 20px 5px 20px;
  font-size: 12px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
  display: flex;
  justify-content: space-around;
  .code {
    width: 20px;
  }
  > span {
    flex: 1;
    text-align: center;
  }
}
.input-icon {
  height: 100%;
  width: 25px;
  text-align: center;
  line-height: 28px;
  transition: 0.3s;
}
.input-icon-del {
  cursor: pointer;
  &:hover {
    color: var(--mainColor);
  }
}
.input-icon-down {
  transform: rotate(180deg);
}
.option-item {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  justify-content: space-around;
  .code {
    width: 20px;
  }
  .option-item-main {
    text-align: center;
    flex: 1;
  }
}

.header-box {
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
  padding: 5px 10px;
}
// 底部的翻页
.footer-page {
  border-top: 1px solid rgba(0, 0, 0, 0.1);
}

.select-list {
  display: flex;
  flex-wrap: wrap;
  padding: 20px;
  max-width: 450px;
  .item {
    margin-right: 20px;
  }
  .none {
    margin: 0 auto;
  }
}
</style>
